"use strict";

const Ws = require("ws");
const Logger = require("pomelo-logger").getLogger("Utils", process.pid, __filename);

const PBJs = require("protobufjs");
const PBJsIns = PBJs.loadSync(__dirname + "/packet/Message.proto");

const PBNativeIns = {
    "Message": require("./packet/Message_pb.js").Message,
    //"GameOver": require("./packet/GameOver_pb.js").GameOver,
    //"SetTarget": require("./packet/SetTarget_pb").SetTarget
};

const protocols = require("./api.json");

let type = protocols.PROTOCOL;
let PacketId = {};
let ClassId = {};
let FunctionId = {};

(function () {
    for (let k in protocols) {
        if (protocols.hasOwnProperty(k) && typeof protocols[k] == "number") {
            PacketId[protocols[k]] = k.replace(/^TO_|^ON_/, "").toLowerCase().replace(/_(\S)/g, (all, letter)=> {
                return letter.toUpperCase()
            });
            ClassId[protocols[k]] = PacketId[protocols[k]].replace(/\b(\w)|\s(\w)/g, m => {
                return m.toUpperCase();
            });
            FunctionId[protocols[k]] = PacketId[protocols[k]].toLowerCase().replace(/\b(\w)|\s(\w)/g, m => {
                return m.toUpperCase();
            });
        }
    }
})();

function decodeData(message) {
    let msg = {};

    switch (type) {
        case "JSON":
            return JSON.parse(message);
            break;
        case "ProtoBufNative": {
            let umessage = new Uint8Array(message, 0, message.length);
            msg = PBNativeIns.Message.deserializeBinary(umessage);

            // Usage:

            Logger.debug("PacketId: %s", msg.getPacketid());
            Logger.debug("TimeStamp: %s", msg.getTimestamp());
            if (msg.getGameover()) {
                Logger.debug("GameOver: [ time: %s, score: %s ]", msg.getGameover().getTime(), msg.getGameover().getScore());
            }
            if (msg.getSettarget()) {
                Logger.debug("SetTarget: [ Radian: %s, Rotation: %s, X: %s, Y: %s ]", msg.getSettarget().getRadian(), msg.getSettarget().getRotation(), msg.getSettarget().getX(), msg.getSettarget().getY());
            }

            // ....
        }
            break;
        case "ProtoBufJS":
            let sender = PBJsIns.lookup("Message");
            msg = sender.decode(message);
            break;
        default:
            break;
    }
    return msg;
}

function encodeData(message) {
    if (!message || typeof message != "object") {
        return null;
    }
    message.timestamp = new Date().valueOf();

    let sender, msg;
    switch (type) {
        case "JSON":
            msg = JSON.stringify(message);
            break;
        case "ProtoBufNative": {
            sender = new PBNativeIns.Message();

            // Usage:

            sender.setPacketid(message.packetId);
            sender.setTimestamp(message.timestamp);

            if (message.setTarget) {
        let setTarget = new PBNativeIns.SetTarget();
        setTarget.setRadian(message.setTarget.radian);
        setTarget.setRotation(message.setTarget.rotation);
        setTarget.setX(message.setTarget.x);
        setTarget.setY(message.setTarget.y);
        sender.setSettarget(setTarget);
    }

            if (message.gameOver) {
                let gameOver = new PBNativeIns.GameOver();
                gameOver.setTime(message.gameOver.time);
                gameOver.setScore(message.gameOver.score);
                sender.setGameover(gameOver);
            }

            // ....

            msg = sender.serializeBinary();

        }
            break;
        case "ProtoBufJS":
            sender = PBJsIns.lookup("Message");
            msg = sender.encode(message).finish();
            break;
        default:
            break;
    }
    return msg;
}

Ws.prototype.SendPacket = function (packetId, message) {
    if (this.readyState === Ws.OPEN) {
        let msg = {};
        msg.packetId = packetId;
        msg[PacketId[packetId]] = message || {};

        let data = encodeData(msg);

        this.send(data, function (err) {
            if (err) {
                Logger.error("Server Send Data To Client Return Error: ", err);
            }
        });
    } else {
        this.readyState = Ws.CLOSED;
        this.emit('close');
        this.removeAllListeners();
    }
};

module.exports = {
    "encode": encodeData,
    "decode": decodeData
};
